package com.hanxiaozhang.idgenerator;

import com.hanxiaozhang.config.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 〈一句话功能简述〉<br>
 * 〈生成分布式id〉
 *
 * @author hanxinghua
 * @create 2022/9/8
 * @since 1.0.0
 */
@Slf4j
@Component
public class No1IdGenerator {

    private static RedisUtil redisStaticUtil;

    private static final String UNIQUE_SERIAL_NO_PREFIX = "%s_%s_%s";

    @Autowired
    private RedisUtil redisUtil;

    @PostConstruct
    public void init() {
        redisStaticUtil = redisUtil;
    }


    private No1IdGenerator() {
    }


    /**
     * 生成分布式唯一id
     *
     * @param projectName
     * @param businessType
     * @param businessId
     * @return
     */
    public static long generatorUniqueSerialNo(String projectName, String businessType, long businessId) {
        long increment = 0L;
        long currentTime = System.currentTimeMillis() / 1000L;
        try {
            String key = String.format(UNIQUE_SERIAL_NO_PREFIX, currentTime, projectName, businessType);
            increment = redisStaticUtil.incr(key, 1L);
            if (increment == 1L) {
                redisStaticUtil.expire(key, 10L);
            }
        } catch (Exception e) {
            log.error("generatorUniqueSerialNo error !", e);
        }
        return Long.valueOf(currentTime << 32 | businessId << 16 | increment);

    }


    /**
     * 批量生成分布式唯一id
     *
     * @param projectName
     * @param businessType
     * @param businessId
     * @param number
     * @return
     */
    public static List<Long> batchGeneratorUniqueSerialNo(String projectName, String businessType, long businessId, int number) {
        long increment = 0L, incrementNumber = 0L;
        long currentTime = System.currentTimeMillis() / 1000L;

        try {
            String key = String.format(UNIQUE_SERIAL_NO_PREFIX, currentTime, projectName, businessType);
            increment = redisStaticUtil.incr(key, (long) number);
            if (increment == (long) number) {
                redisStaticUtil.expire(key, 10L);
            }
        } catch (Exception e) {
            log.error("batchGeneratorUniqueSerialNo error is ", e);
        }

        List<Long> list = new ArrayList();

        for (int i = 0; i < number; ++i) {
            incrementNumber = increment - (long) i;
            list.add(Long.valueOf(currentTime << 32 | businessId << 16 | incrementNumber));
        }

        return list;
    }


    /**
     * 获取时间，精确到秒
     *
     * @param uniqueSerialNo
     * @return
     */
    public static Long getCurrentTime(Long uniqueSerialNo) {
        // 无符号右移32位
        return (uniqueSerialNo >>> 32) * 1000;
    }

    /**
     * 获取BusinessId
     *
     * @param uniqueSerialNo
     * @return
     */
    public static Long getBusinessId(Long uniqueSerialNo) {
        // 左移32位，把currentTime弄没，然后再无符号右移48位
        return uniqueSerialNo << 32 >>> 48;
    }

    /**
     * 获取incrementNum
     *
     * @param uniqueSerialNo
     * @return
     */
    public static Long getIncrementNum(Long uniqueSerialNo) {
        // 左移48位，把currentTime、businessId弄没，然后再无符号右移48位
        return uniqueSerialNo << 48 >>> 48;
    }


    public static void main(String[] args) {

        System.out.println(getCurrentTime(7125672502075850753L));
        System.out.println(new Date(getCurrentTime(7125672502075850753L)));
        System.out.println(getBusinessId(7125672502075850753L));
        System.out.println(getIncrementNum(7125672502075850753L));

    }
}
